#include"Bmp.h"
/****************************************************************************
 *  Name       : decodeBMP
 *  Parameters : Source image filename and
 *               Target filename (to screen by default)
 *  Returns    : Errorcode
 *  Purpose    : To decode 8/24 bit BMP's encoded using encodeBMP or
 *               encodeBMP24
 ****************************************************************************/
int decodeBMP(char *imgSrc, char *target = "STEGO.SWP")
{
	FILE *ImgFile, *Target;
	bmpHeaderType bmp;
	int  Flag = 0;
	long offset = BMP_OFFSET;
	char c, key;

	if ( strlen(imgSrc) <= 0 )
	{
		// Bad source filename
		return ERR_BF;
	}
	else if ( strlen(target) <= 0 )
	{
		// Bad target filename
		return ERR_BF;
	}
	else // good filename
	{
		ImgFile = fopen(imgSrc, "rb");
		Target  = fopen(target, "w+");
		if ( !ImgFile )
		{
			// Image file not found
			return ERR_FNF;
		}
		else if ( !Target )
		{
			// Could not open target
			fclose(ImgFile);
			return ERR_FNF;
		}
		else // good file pointer
		{
			if ( !fread(&bmp, sizeof(bmp), 1, ImgFile) )
			{
				// Bad fread operation
				fclose(ImgFile);
				fclose(Target);
				return ERR_URH;
			}
			else
			{
				offset = bmp.pixelOffset;
				c = decodeChar(ImgFile, offset); // decode char at offset from image
				if ( c == STX )
				{
					while ( c != EOF )
					{
						offset += 8;
						c = decodeChar(ImgFile, offset);
						if ( !Flag )
						{
							key = c;
							Flag = 1;
						}
						else
							c ^= key;
						if ( c == ETX )
							break;
						if ( fputc(c, Target) == EOF )
						{ // bad fputc
							fclose(ImgFile);
							fclose(Target);
							return ERR_WRE;
						}
					} // endwhile
					fclose(ImgFile);
					fclose(Target);
					return SUCCESS; // Success !!
				}
				else // Nothing encoded by me in the file
				{
					// No Message to be decoded
					fclose(ImgFile);
					fclose(Target);
					return ERR_NM;
				}
			} // Endif (bad file ptr)
		} // Endif (bad fread)
	} // Endif (bad filename)
}
/****************************************************************************
 *  Name       : encodeBMP
 *  Parameters : Source image filename,
 *               Message text filename and
 *               Target image filename
 *  Returns    : Errorcode
 *  Purpose    : Used to encode 8-bit BMP's (Each message byte is encoded in
 *               LSB's of 8 bytes of image data)
 ****************************************************************************/
int encodeBMP(char *imgSrc, char *txtSrc, char *img)
{
	char key;
	int i, ch;
	long MaxE, offset;
	bmpHeaderType bmp;
	FILE *ImgFile, *TxtFile, *Target;
	ImgFile = fopen(imgSrc, "rb"); // open image into which text is encoded
	TxtFile = fopen(txtSrc, "rb"); // open text to encode
	if ( !ImgFile ) // bad image file
	{
		// Bad image file
		//printf("Error: %s not found\n", imgSrc);
		return ERR_FNF;
	}
	else if ( !TxtFile ) // bad text file
	{
		// Bad message file
		// printf("Error: %s not found\n", txtSrc);
		fclose(ImgFile);
		return ERR_FNF;
	}
	else // image and text file are both healthy
	{
		MaxE = maxEncodeBMP(ImgFile); // get maximum encodable size for image
		if ( MaxE < fileSize(TxtFile) ) // too much text
		{
			// Message file too big
			// printf("Maximum encodable data is %d bytes\n", MaxE);
			fclose(ImgFile);
			fclose(TxtFile);
			return ERR_M2B;
		}
		else // Looking good
		{
			fseek(ImgFile, 0L, SEEK_SET); // reset image file pointer to start
			if ( !fread(&bmp, BMP_HEADER, 1, ImgFile) ) // read header
			{
				// Bad fread operation
				// printf("Invalid fread\n");
				fclose(ImgFile);
				fclose(TxtFile);
				return ERR_URH;
			}
			// Validate header info
			if ( (bmp.bitsPerPixel == 8) || (bmp.nPlanes == 1)
																	 || (bmp.compression == 0)
																	 || (bmp.c1 == 'B')
																	 || (bmp.c2 == 'M') )
			{
				Target = fopen(img, "wb"); // Open target image
				fwrite(&bmp, BMP_HEADER, 1, Target); // Copy header info
				fseek(Target, BMP_HEADER, SEEK_SET);
				for ( i = 0; i < bmp.pixelOffset - BMP_HEADER; i++ )
				{ // Copy color table info
					ch = fgetc(ImgFile);
					fputc(ch, Target);
				}
				offset = bmp.pixelOffset;
				offset = encodeChar(ImgFile, offset, Target, STX); // Encode STX
				fseek(TxtFile, 0L, SEEK_SET); // Reset text file to start
				ch = fgetc(TxtFile); // Get first char
				key = ch;

				while ( offset != -1) // Main encode loop
				{
					offset = encodeChar(ImgFile, offset, Target, ch);
															// Encode ch and get new offset value
					ch = fgetc(TxtFile); // Read next char from text file
					if ( ch == EOF)
						break;
					ch ^= key;
				} // endwhile

				offset = encodeChar(ImgFile, offset, Target, ETX ^ key);
														// Encode ETX to finish off
				if ( offset == -1 ) // error in encoding
				{
					// Unable to write to file
					fclose(ImgFile);
					fclose(TxtFile);
					fclose(Target);
					return ERR_WRE;
				}
				else   // Everything was successful
				{
					fclose(TxtFile); // No more use for txt file
					fseek(ImgFile, offset, SEEK_SET);
					fseek(Target, offset, SEEK_SET);
					ch = fgetc(ImgFile);
					while ( ch != EOF ) // Copy rest of image file to target
					{
						fputc(ch, Target);
						ch = fgetc(ImgFile);
					}
					fclose(ImgFile); // close up !
					fclose(Target);
					return SUCCESS; // Success !!
				}
			}
			else
			{
				// Bad BMP
				fclose(ImgFile);
				fclose(TxtFile);
				return ERR_BMP;
			} // endif (validate  BMP Header)
		} // endif (maxE < text file size)
	} // good image and text filez
}
/****************************************************************************
 *  Name       : encodeBMP24
 *  Parameters : Source image filename,
 *               Message text filename and
 *               Target image filename
 *  Returns    : Errorcode
 *  Purpose    : Used to encode 24-bit BMP's (Each message byte is encoded
 *               in LSB's of 8 bytes of image data)
 ****************************************************************************/
int encodeBMP24(char *imgSrc, char *txtSrc, char *img)
{
	char key;
	int i, ch;
	long MaxE, offset;
	bmpHeaderType bmp;
	FILE *ImgFile, *TxtFile, *Target;
	ImgFile = fopen(imgSrc, "rb"); // open image into which text is encoded
	TxtFile = fopen(txtSrc, "rb"); // open text to encode
	if ( !ImgFile ) // bad image file
	{
		// Image file not found
		// printf("Error: %s not found\n", imgSrc);
		return ERR_FNF;
	}
	else if ( !TxtFile ) // bad text file
	{
		// Message file not found
		// printf("Error: %s not found\n", txtSrc);
		fclose(ImgFile);
		return ERR_FNF;
	}
	else // image and text file are both healthy
	{
		MaxE = maxEncodeBMP(ImgFile); // get maximum encodable size for image
		if ( MaxE < fileSize(TxtFile) ) // too much text
		{
			// Message file too big
			// printf("Maximum encodable data is %d bytes\n", MaxE);
			fclose(ImgFile);
			fclose(TxtFile);
			return ERR_M2B;
		}
		else // Looking good
		{
			fseek(ImgFile, 0L, SEEK_SET); // reset image file pointer to start
			if ( !fread(&bmp, BMP_HEADER, 1, ImgFile) ) // read header
			{
				// Bad fread operation
				// printf("Invalid fread\n");
				fclose(ImgFile);
				fclose(TxtFile);
				return ERR_URH;
			}
			if ( (bmp.bitsPerPixel == 24) &&
					 (bmp.nPlanes == 1)       &&
					 (bmp.compression == 0)   &&
					 (bmp.c1 == 'B')          &&
					 (bmp.c2 == 'M') ) // Validate header info
			{
				Target = fopen(img, "wb"); // Open target image
				fwrite(&bmp, BMP_HEADER, 1, Target); // Copy header info
				fseek(Target, BMP_HEADER, SEEK_SET);
				for ( i = 0; i < bmp.pixelOffset - BMP_HEADER; i++ )
				{
					ch = fgetc(ImgFile);
					fputc(ch, Target);
				}
				offset = bmp.pixelOffset;
				offset = encodeChar(ImgFile, offset, Target, STX); // Encode STX
				fseek(TxtFile, 0L, SEEK_SET); // Reset text file to start
				ch = fgetc(TxtFile); // Get first char
				key = ch;

				while ( offset != -1) // Main encode loop
				{
					offset = encodeChar(ImgFile, offset, Target, ch);
															// Encode ch and get new offset value
					ch = fgetc(TxtFile); // Read next char from text file
					if ( ch == EOF )
						break;
					ch ^= key;
				} // endwhile

				offset = encodeChar(ImgFile, offset, Target, ETX ^ key);
														// Encode ETX to finish off
				if ( offset == -1 ) // error in encoding
				{
					// Unable to write to file
					// printf("Error writing to file\n");
					fclose(ImgFile);
					fclose(TxtFile);
					return ERR_WRE;
				}
				else   // Everything was successful
				{
					fclose(TxtFile); // No more use for txt file
					fseek(ImgFile, offset, SEEK_SET);
					fseek(Target, offset, SEEK_SET);
					ch = fgetc(ImgFile);
					while ( ch != EOF ) // Copy rest of image file to target
					{
						fputc(ch, Target);
						ch = fgetc(ImgFile);
					}
					fclose(ImgFile); // close up !
					fclose(Target);
					return SUCCESS; // Success !!
				}
			}
			else
			{
				// Bad BMP
				fclose(ImgFile);
				fclose(TxtFile);
				return ERR_BMP;
			} // endif (validate  BMP Header)
		} // endif (maxE < text file size)
	} // good image and text filez
}

/****************************************************************************
 *  Name       : validBMP
 *  Parameters : Image filename
 *  Returns    : 0 if invalid else 1 on success
 *  Purpose    : To check if the Image file can be encoded by this program.
 ****************************************************************************/
int validBMP(char *image)
{
	 FILE *fp;
	 bmpHeaderType bmp;
	 fp = fopen(image, "rb");
	 if ( fp )
	 {
		 if ( fread(&bmp, sizeof(bmp), 1, fp) )
		 {
			 if ( (bmp.bitsPerPixel != 8 && bmp.bitsPerPixel !=24)
																 || (bmp.nPlanes != 1)
																 || (bmp.compression != 0)
																 || (bmp.c1 != 'B')
																 || (bmp.c2 != 'M') )
			 {
				 fclose(fp);
				 return 0;
			 }
			 fclose(fp);
			 return bmp.bitsPerPixel;
		 }
		 // Bad or no BMP header
		 // cout<<"Error: Invalid or corrupted Bitmap file"<<endl;
		 return 0;
	 }
	 // File not found
	 // cout<<"Error: "<<image<<" - File not found"<<endl;
	 return 0;
}

/****************************************************************************
 *  Name       : maxEncode
 *  Parameters : File pointer (of image file)
 *  Returns    : Maximum encodable size of data in bytes
 *  Purpose    : To determine the maximum encodable amount of data in the
 *               given BMP
 ****************************************************************************/
long maxEncodeBMP(FILE *fp)
{
	long size;
	fseek(fp, 0L, SEEK_END);
	size = ftell(fp);
	size -= BMP_OFFSET;
	size /= 8; // Since each byte requires 8 bytes from image file for encode
	size -= 2; // Account for STX and ETX
	return size;
}
/****************************************************************************
 *  Name       : fileSize
 *  Parameters : File pointer (usually message file)
 *  Returns    : The filesize in bytes
 *  Purpose    : To determine the size of the message file
 ****************************************************************************/
long fileSize(FILE *fp)
{
	fseek(fp, 0L, SEEK_END); // Get filesize
	long siz = ftell(fp);
	return siz;
}
/****************************************************************************
 *  Name       : encodeChar
 *  Parameters : File pointer of source image
 *							 Position / offset where the current encode must take place
 *               File pointer of target image
 *               Character to be encoded
 *  Returns    : New offset value on success or -1 on failure
 *  Purpose    : To encode a character of message into the target image
 ****************************************************************************/
long encodeChar(FILE *source, long Off, FILE *target, char c)
{                                         // Encode to target
	/* To store the Bit Filters */
	char Binary[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100};
	int i, ch;
	fseek(source, Off, SEEK_SET); // Set both files pointers to offset position
	fseek(target, Off, SEEK_SET);
	for ( i = 0; i < 8; i++ ) // Move throught 8 consecutive bytes
	{
		ch = fgetc(source);
		ch = (c & Binary[i]) ? ch | 0x1 : ch & 0xfe; // Mirror bit value at ith
												// position in c in the LSB's of the image file
		if ( fputc(ch, target) == -1 )
			return -1; // Bad fputc
	}
	return (Off + 8); // Return new offset value
}
/****************************************************************************
 *  Name       : decodeChar
 *  Parameters : File pointer (image file)
 *               Position / offset
 *  Returns    : The character that was decoded from that position
 *  Purpose    : To decode 1 message byte from encoded file at the offset
 ****************************************************************************/
char decodeChar(FILE *fp, long pos)
{
	/* To store the Bit Filters */
	char Binary[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100};
	int i;
	char byte = 0;
	fseek(fp, pos, SEEK_SET);
	for ( i = 0; i < 8; i++ ) // Get byte from 8 bytes of image file
	{
		byte += (fgetc(fp) & 0x1) ? Binary[i] : 0;
	}
	return byte;
}
